/**全局背景音乐Bgm，音效Effect播放控制模块  
 *所有Bgm和Effect都放在resources文件夹下面动态加载，推荐的默认文件夹结构为：  
 *--resources  （resources下有个文件夹Audio，用来放所有音乐文件）  
  ----Audio  （Audio下有2个文件夹Bgm，Effect）    
  ------Bgm  （Bgm文件夹下用来放所有Bgm，第一个音乐为默认Bgm）    
  ------Effect  （Effect文件夹用来放所有Effect）    
 *如果不为上面的默认文件结构，可以在 init函数 里面设置你需要的默认参数
 */
let GAudio = new class {

    /**保存所有音乐文件的根目录，相对"resources/"目录  "Audio"*/
    private _AudioPath = "Audio";

    /**==========  Bgm ==========*/
    /**保存所有Bgm的根目录，相对`resources/${_AudioPath}/` 目录  "Bgm" */
    private _BgmPath = "Bgm";
    /**默认Bgm的音量大小  1 */
    private _BgmVolume = 1;
    /**默认Bgm是否循环  true */
    private _BgmLoop = true;
    /**默认播放的Bgm的名字 "Bgm1"*/
    private _BgmDefault = "";
    /**是否允许多个Bgm同时播放 false */
    private _BgmCanMultiple = false;

    /**==========  Effect ==========*/
    /**保存所有Effect的根目录，相对`resources/${_AudioPath}/` 目录  "Effect"  */
    private _EffectPath = "Effect";
    /**默认Effect的音量大小  1 */
    private _EffectVolume = 1;
    /**默认Effect是否循环  false */
    private _EffectLoop = false;
    /**是否允许多个Effect同时播放 true */
    private _EffectCanMultiple = true;


    /**保存所有Audio信息的map */
    private _allAudioInfo: Map<string, cc.AudioClip> = new Map();
    /**保存正在播放的Bgm的map */
    private _playingBgm: Map<string, number> = new Map();
    /**保存正在播放的Effect的map */
    private _playingEffect: Map<string, number> = new Map();

    /**是否为静音模式 false */
    private _isMute = false;


    /**初始化设置Bgm和Effect的默认参数，一般都用默认的默认参数，  
     * - ***可不调用此函数***  
     * @param optional 属性参数说明如下：
     * @param optional.AudioPath 保存所有Bgm的根目录，默认为"Audio"  
     * @param optional.BgmPath 保存所有Bgm的根目录，默认为"Bgm"  
     * @param optional.BgmVolume 默认Bgm的音量大小，默认为1  
     * @param optional.BgmLoop 默认Bgm是否循环，默认为true  
     * @param optional.BgmDefault 默认播放的Bgm的名字，如: "Bgm1"，默认为${_BgmPath}目录下的第一个文件  
     * @param optional.BgmCanMultiple 是否允许多个Bgm同时播放 默认为false    
     * @param optional.EffectPath 保存所有Effect的根目录，默认为"Effect"  
     * @param optional.EffectVolume 默认Effect的音量大小，默认为1  
     * @param optional.EffectLoop 默认Effect是否循环，默认为false   
     * @param optional.EffectCanMultiple 是否允许多个Effect同时播放 默认为true   
     * @param optional.isMute 默认是否为静音模式，默认为false   
     */
    init(optional: {
        AudioPath?: string, BgmPath?: string, BgmVolume?: number, BgmLoop?: boolean, BgmDefault?: string, BgmCanMultiple?: boolean,
        EffectPath?: string, EffectVolume?: number, EffectLoop?: boolean, EffectCanMultiple?: boolean, isMute?: boolean,
    } = {}) {
        //设置了默认参数的替换默认的默认参数
        for (let key of Object.keys(optional)) {
            if (typeof this["_" + key] != "undefined") {
                this["_" + key] = optional[key];
            }
        }
    }

    /**加载一个Audio（Bgm或者Effect）  
     * @param path Audio的相对路径
     */
    private _loadAudio(path: string) {
        return new Promise((resolve, reject) => {
            cc.loader.loadRes(path, cc.AudioClip, (err, clip: cc.AudioClip) => {
                if (err) {
                    console.error(err.message || err);
                    reject(err);
                    return;
                }
                console.warn("预加载成功!", clip);
                if (!this._allAudioInfo.has(path)) {
                    //记录当前的Audio
                    this._allAudioInfo.set(path, clip);
                }
                resolve(clip);
            });
        })
    }

    /**预加载一个或多个Bgm  
     * @param name Bgm的名字或者名字数组 
     * @example
     * GAudio.preloadBgm("Bgm1");
     */
    preloadBgm(name: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._loadAudio(`${this._AudioPath}/${this._BgmPath}/${name}`);
            }
        } else {
            this._loadAudio(`${this._AudioPath}/${this._BgmPath}/${name}`);
        }
    }

    /**预加载所有Bgm  
     * @example
     * GAudio.preloadAllBgm();
     */
    preloadAllBgm() {
        return new Promise((resolve, reject) => {
            cc.loader.loadResDir(`${this._AudioPath}/${this._BgmPath}`, (err, clips, urls: string[]) => {
                if (err) {
                    console.error(err.message || err);
                    reject(err);
                    return;
                }
                for (let one of urls) {
                    this._loadAudio(one);
                }
                //如果没指定默认Bgm就选第一个Bgm作为默认
                if (this._BgmDefault == "") {
                    let url = urls[0].split("/");
                    this._BgmDefault = url[url.length - 1];
                }
                resolve(this._BgmDefault);
            });
        });
    }

    /**预加载一个或多个Effect  
     * @param name Effect的名字或者名字数组 
     * @example
     * GAudio.preloadEffect("effect1");
     */
    preloadEffect(name: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._loadAudio(`${this._AudioPath}/${this._EffectPath}/${name}`);
            }
        } else {
            this._loadAudio(`${this._AudioPath}/${this._EffectPath}/${name}`);
        }
    }

    /**预加载所有Effect  
     * @example
     * GAudio.preloadAllEffect();
     */
    preloadAllEffect() {
        cc.loader.loadResDir(`${this._AudioPath}/${this._EffectPath}`, (err, clip, urls: string[]) => {
            if (err) {
                console.error(err.message || err);
                return;
            }
            for (let one of urls) {
                this._loadAudio(one);
            }
        });
    }

    /**预加载所有Audio（Bgm和Effect）
     * @example
     * GAudio.preloadAll();
     */
    preloadAll() {
        this.preloadAllBgm();
        this.preloadAllEffect();
    }


    /**播放Bgm  
     * @param name Bgm名字   
     * @param isLoop Bgm是否循环  
     * @param volume Bgm音量  
     */
    private _playBgm(name: string, isLoop: boolean, volume: number) {
        let path = `${this._AudioPath}/${this._BgmPath}/${name}`;
        let clip = this._allAudioInfo.get(path);
        if (clip) {
            this._playBgmAndSet(clip, isLoop, volume, path);
        } else {//还没加载过，开始加载并播放
            console.warn("还没加载过，开始加载并播放");
            let load = this._loadAudio(path);
            load.then((clip: cc.AudioClip) => {
                this._playBgmAndSet(clip, isLoop, volume, path);
            });
        }
    }

    /**播放Bgm并设置播放信息    
     * @param clip Bgm播放的AudioClip  
     * @param isLoop Bgm是否循环  
     * @param volume Bgm音量  
     * @param path Bgm路径    
     */
    private _playBgmAndSet(clip: cc.AudioClip, isLoop: boolean, volume: number, path: string) {
        if (!this._BgmCanMultiple) {
            this.stopBgm();
        }
        let audioID = cc.audioEngine.play(clip, isLoop, volume);
        this._playingBgm.set(path, audioID);
        cc.audioEngine.setFinishCallback(audioID, () => {
            //播放完清理map
            this._playingBgm.delete(path);
        });
    }

    /**播放背景音乐   所有默认行为都可以在init里面设置     
     * @param name Bgm名字，默认为init里面指定的BgmDefault，没有就默认为Bgm文件夹下第一个文件名字  
     * @param isLoop Bgm是否循环播放，默认为true  
     * @param volume Bgm的音量，默认为1  
     * @example
     * GAudio.playBgm();  
     * GAudio.playBgm("Bgm1");  
     */
    playBgm(name = this._BgmDefault, isLoop = this._BgmLoop, volume = this._BgmVolume) {
        if (this._isMute) return;
        //如果还没指定默认播放音乐就动态加载
        if (!name) {
            console.warn("如果还没指定默认播放音乐就动态加载");
            this.preloadAllBgm().then((name: string) => {
                this._playBgm(name, isLoop, volume);
            });
        } else {
            this._playBgm(name, isLoop, volume);
        }
    }

    /**停止正在播放的背景音乐  
     * @param name Bgm的名字或者名字数组，可以为空，为空时表示停止所有Bgm  
     * @example  
     * GAudio.stopBgm();
     */
    stopBgm(name?: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._stopBgm(one);
            }
        } else {
            if (typeof name == "undefined") {//停止所有Bgm 
                this._playingBgm.forEach((v, k) => {
                    let url = k.split("/");
                    this._stopBgm(url[url.length - 1]);
                })
            } else {
                this._stopBgm(name);
            }
        }
    }

    /**停止正在播放的背景音乐  
     * @param name Bgm的名字
     */
    private _stopBgm(name: string) {
        let path = `${this._AudioPath}/${this._BgmPath}/${name}`;
        let audioID = this._playingBgm.get(path);
        cc.audioEngine.stop(audioID);
        this._playingBgm.delete(path);
    }

    /**暂停正在播放的背景音乐   
     * @param name Bgm的名字或者名字数组，可以为空，为空时表示暂停所有Bgm  
     * @example  
     * GAudio.pauseBgm();
     */
    pauseBgm(name?: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._pauseBgm(one);
            }
        } else {
            if (typeof name == "undefined") {//暂停所有Bgm 
                this._playingBgm.forEach((v, k) => {
                    let url = k.split("/");
                    this._pauseBgm(url[url.length - 1]);
                })
            } else {
                this._pauseBgm(name);
            }
        }
    }

    /**暂停正在播放的背景音乐  
     * @param name Bgm的名字
     */
    private _pauseBgm(name: string) {
        let path = `${this._AudioPath}/${this._BgmPath}/${name}`;
        let audioID = this._playingBgm.get(path);
        cc.audioEngine.pause(audioID);
    }

    /**恢复背景音乐   
     * @param name Bgm的名字或者名字数组，可以为空，为空时表示恢复所有Bgm  
     * @example  
     * GAudio.resumeBgm();
     */
    resumeBgm(name?: string | string[]) {
        if (this._isMute) return;
        if (Array.isArray(name)) {
            for (let one of name) {
                this._resumeBgm(one);
            }
        } else {
            if (typeof name == "undefined") {//恢复所有Bgm 
                this._playingBgm.forEach((v, k) => {
                    let url = k.split("/");
                    this._resumeBgm(url[url.length - 1]);
                })
            } else {
                this._resumeBgm(name);
            }
        }
    }

    /**恢复背景音乐  
     * @param name Bgm的名字
     */
    private _resumeBgm(name: string) {
        let path = `${this._AudioPath}/${this._BgmPath}/${name}`;
        let audioID = this._playingBgm.get(path);
        cc.audioEngine.resume(audioID);
    }

    /**播放音效   所有默认行为都可以在init里面设置     
     * @param name Effect名字 
     * @param isLoop Effect是否循环播放，默认为false
     * @param volume Effect的音量，默认为1  
     * @example
     * GAudio.playEffect("effect1");  
     */
    playEffect(name: string, isLoop = this._EffectLoop, volume = this._EffectVolume) {
        if (this._isMute) return;
        let path = `${this._AudioPath}/${this._EffectPath}/${name}`;
        let clip = this._allAudioInfo.get(path);
        if (clip) {
            this._playEffectAndSet(clip, isLoop, volume, path);
        } else {//还没加载过，开始加载并播放
            console.warn("Effect还没加载过，开始加载并播放");
            let load = this._loadAudio(path);
            load.then((clip: cc.AudioClip) => {
                this._playEffectAndSet(clip, isLoop, volume, path);
            });
        }
    }

    /**播放Effect并设置播放信息    
     * @param clip Effect播放的AudioClip  
     * @param isLoop Effect是否循环  
     * @param volume Effect音量  
     * @param path Effect路径    
     */
    private _playEffectAndSet(clip: cc.AudioClip, isLoop: boolean, volume: number, path: string) {
        if (!this._EffectCanMultiple) {
            this.stopEffect();
        }
        let audioID = cc.audioEngine.play(clip, isLoop, volume);
        this._playingEffect.set(path, audioID);
        cc.audioEngine.setFinishCallback(audioID, () => {
            //播放完清理map
            this._playingEffect.delete(path);
        });
    }

    /**停止正在播放的音效   
     * @param name Effect的名字或者名字数组，可以为空，为空时表示停止所有Effect  
     * @example  
     * GAudio.stopEffect();
     */
    stopEffect(name?: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._stopEffect(one);
            }
        } else {
            if (typeof name == "undefined") {//停止所有Effect 
                this._playingEffect.forEach((v, k) => {
                    let url = k.split("/");
                    this._stopEffect(url[url.length - 1]);
                })
            } else {
                this._stopEffect(name);
            }
        }
    }

    /**停止正在播放的音效 
     * @param name Effect的名字
     */
    private _stopEffect(name: string) {
        let path = `${this._AudioPath}/${this._EffectPath}/${name}`;
        let audioID = this._playingEffect.get(path);
        cc.audioEngine.stop(audioID);
        this._playingEffect.delete(path);
    }

    /**暂停正在播放的音效   
     * @param name Effect的名字或者名字数组，可以为空，为空时表示暂停所有Effect  
     * @example  
     * GAudio.pauseEffect();
     */
    pauseEffect(name?: string | string[]) {
        if (Array.isArray(name)) {
            for (let one of name) {
                this._pauseEffect(one);
            }
        } else {
            if (typeof name == "undefined") {//暂停所有Effect 
                this._playingEffect.forEach((v, k) => {
                    let url = k.split("/");
                    this._pauseEffect(url[url.length - 1]);
                })
            } else {
                this._pauseEffect(name);
            }
        }
    }

    /**暂停正在播放的音效  
     * @param name Effect的名字
     */
    private _pauseEffect(name: string) {
        let path = `${this._AudioPath}/${this._EffectPath}/${name}`;
        let audioID = this._playingEffect.get(path);
        cc.audioEngine.pause(audioID);
    }

    /**恢复音效   
     * @param name Effect的名字或者名字数组，可以为空，为空时表示恢复所有Effect  
     * @example  
     * GAudio.resumeEffect();
     */
    resumeEffect(name?: string | string[]) {
        if (this._isMute) return;
        if (Array.isArray(name)) {
            for (let one of name) {
                this._resumeEffect(one);
            }
        } else {
            if (typeof name == "undefined") {//恢复所有Effect 
                this._playingEffect.forEach((v, k) => {
                    let url = k.split("/");
                    this._resumeEffect(url[url.length - 1]);
                })
            } else {
                this._resumeEffect(name);
            }
        }
    }

    /**恢复音效  
     * @param name Effect的名字
     */
    private _resumeEffect(name: string) {
        let path = `${this._AudioPath}/${this._EffectPath}/${name}`;
        let audioID = this._playingEffect.get(path);
        cc.audioEngine.resume(audioID);
    }

    /**停止所有Audio（Bgm和Effect）  
     * @example
     * GAudio.stopAll();
      */
    stopAll() {
        this.stopBgm();
        this.stopEffect();
    }

    /**暂停所有Audio（Bgm和Effect）  
     * @example
     * GAudio.pauseAll();
      */
    pauseAll() {
        this.pauseBgm();
        this.pauseEffect();
    }

    /**恢复所有Audio（Bgm和Effect） 
     * @example
     * GAudio.resumeAll();
     */
    resumeAll() {
        if (this._isMute) return;
        this.resumeBgm();
        this.resumeEffect();
    }


    /**设置声音模式  
     * @param isMute 是否为静音模式，  
     * true表示设置为静音，会暂停所有背景音乐和音效，并且不能播放和恢复所有Audio    
     * false表示取消静音，会恢复所有的背景音乐和音效，并且能播放和恢复其他Audio
     * @example  
     * GAudio.setAudioMode(true);
     */
    setAudioMode(isMute: boolean) {
        this._isMute = isMute;
        if (this._isMute) {
            this.pauseAll();
        } else {
            this.resumeAll();
        }
    }

    /**切换当前的声音模式，返回当前是否为静音模式   
     * @example
     * GAudio.audioModeChange();
     */
    audioModeChange() {
        let isMute = !this._isMute;
        this.setAudioMode(isMute);
        return isMute;
    }

    /**获取当前的是否为静音状态 
     * @example
     * GAudio.getIsMute();
     */
    getIsMute() {
        return this._isMute;
    }

}();
window["GAudio"] = GAudio;